<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 2.0.26">
<link rel="icon" type="image/svg" href="../../../../_docs/_Assets/Logo.svg">
<title>RatingControl Sample</title>
<style>
@import url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css);

:root {
    --background: #fff;
    --black-color: #000;
    --background-yellow: #ff0;
    --border: #c0c0c0;
    --gray-color: #6c818f;
    --link-color-default: var(--accent-primary-color);
    --foreground-color: #222222;
    --accent-primary-color: #3578e5;
    --accent-secondary-color: #102445;
    --border2: #dddddd;
    --block-quote-color: #748590;
    --pre-background-color: #f2f2f2;
    --dark-gray-color: #333333;
    --kdb-background-color: #F7F7F7;
    --header-color: var(--accent-primary-color);
    --link-hover-color: #102445;
    --toc-border-color: #d9d9d9;
    --exampleblock-border-color: #e6e6e6;
    --em-color: #373737;
    --admotion-background-color: #e6e6e6;
    --sidebar-background-color: #e8ecef;
    --border-radius-default: 6px;
}

/* normalize.css v2.1.1 | MIT License | git.io/normalize */
/* ========================================================================== HTML5 display definitions ========================================================================== */
/** Correct `block` display not defined in IE 8/9. */
article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary {
    display: block;
}

/** Correct `inline-block` display not defined in IE 8/9. */
audio, canvas, video {
    display: inline-block;
}

/** Prevent modern browsers from displaying `audio` without controls. Remove excess height in iOS 5 devices. */
audio:not([controls]) {
    display: none;
    height: 0;
}

/** Address styling not present in IE 8/9. */
[hidden] {
    display: none;
}

/* ========================================================================== Base ========================================================================== */
/** 1. Prevent system color scheme's background color being used in Firefox, IE, and Opera. 2. Prevent system color scheme's text color being used in Firefox, IE, and Opera. 3. Set default font family to sans-serif. 4. Prevent iOS text size adjust after orientation change, without disabling user zoom. */
html {
    background: var(--background); /* 1 */
    color: var(--black-color); /* 2 */
    font-family: sans-serif; /* 3 */
    -ms-text-size-adjust: 100%; /* 4 */
    -webkit-text-size-adjust: 100%; /* 4 */
}

/** Remove default margin. */
body {
    margin: 0;
}

/* ========================================================================== Links ========================================================================== */
/** Address `outline` inconsistency between Chrome and other browsers. */
a:focus {
    outline: thin dotted;
}

/** Improve readability when focused and also mouse hovered in all browsers. */
a:active, a:hover {
    outline: 0;
}

/* ========================================================================== Typography ========================================================================== */
/** Address variable `h1` font-size and margin within `section` and `article` contexts in Firefox 4+, Safari 5, and Chrome. */
h1 {
    font-size: 2em;
    margin: 0.67em 0;
}

/** Address styling not present in IE 8/9, Safari 5, and Chrome. */
abbr[title] {
    border-bottom: 1px dotted;
}

/** Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. */
b, strong {
    font-weight: bold;
}

/** Address styling not present in Safari 5 and Chrome. */
dfn {
    font-style: italic;
}

/** Address differences between Firefox and other browsers. */
hr {
    -moz-box-sizing: content-box;
    box-sizing: content-box;
    height: 0;
}

/** Address styling not present in IE 8/9. */
mark {
    background: var(--background-yellow);
    color: var(--black-color);
}

/** Correct font family set oddly in Safari 5 and Chrome. */
code, kbd, pre, samp {
    font-family: monospace, serif;
    font-size: 1em;
}

/** Improve readability of pre-formatted text in all browsers. */
pre {
    white-space: pre-wrap;
}

/** Set consistent quote types. */
q {
    quotes: "\201C" "\201D" "\2018" "\2019";
}

/** Address inconsistent and variable font size in all browsers. */
small {
    font-size: 80%;
}

/** Prevent `sub` and `sup` affecting `line-height` in all browsers. */
sub, sup {
    font-size: 75%;
    line-height: 0;
    position: relative;
    vertical-align: baseline;
}

sup {
    top: -0.5em;
}

sub {
    bottom: -0.25em;
}

/* ========================================================================== Embedded content ========================================================================== */
/** Remove border when inside `a` element in IE 8/9. */
img {
    border: 0;
}

/** Correct overflow displayed oddly in IE 9. */
svg:not(:root) {
    overflow: hidden;
}

/* ========================================================================== Figures ========================================================================== */
/** Address margin not present in IE 8/9 and Safari 5. */
figure {
    margin: 0;
}

/* ========================================================================== Forms ========================================================================== */
/** Define consistent border, margin, and padding. */
fieldset {
    border: 1px solid var(--border);
    margin: 0 2px;
    padding: 0.35em 0.625em 0.75em;
}

/** 1. Correct `color` not being inherited in IE 8/9. 2. Remove padding so people aren't caught out if they zero out fieldsets. */
legend {
    border: 0; /* 1 */
    padding: 0; /* 2 */
}

/** 1. Correct font family not being inherited in all browsers. 2. Correct font size not being inherited in all browsers. 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. */
button, input, select, textarea {
    font-family: inherit; /* 1 */
    font-size: 100%; /* 2 */
    margin: 0; /* 3 */
}

/** Address Firefox 4+ setting `line-height` on `input` using `!important` in the UA stylesheet. */
button, input {
    line-height: normal;
}

/** Address inconsistent `text-transform` inheritance for `button` and `select`. All other form control elements do not inherit `text-transform` values. Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. Correct `select` style inheritance in Firefox 4+ and Opera. */
button, select {
    text-transform: none;
}

/** 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` and `video` controls. 2. Correct inability to style clickable `input` types in iOS. 3. Improve usability and consistency of cursor style between image-type `input` and others. */
button, html input[type="button"], input[type="reset"], input[type="submit"] {
    -webkit-appearance: button; /* 2 */
    cursor: pointer; /* 3 */
}

/** Re-set default cursor for disabled elements. */
button[disabled], html input[disabled] {
    cursor: default;
}

/** 1. Address box sizing set to `content-box` in IE 8/9. 2. Remove excess padding in IE 8/9. */
input[type="checkbox"], input[type="radio"] {
    box-sizing: border-box; /* 1 */
    padding: 0; /* 2 */
}

/** 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome (include `-moz` to future-proof). */
input[type="search"] {
    -webkit-appearance: textfield; /* 1 */
    -moz-box-sizing: content-box;
    -webkit-box-sizing: content-box; /* 2 */
    box-sizing: content-box;
}

/** Remove inner padding and search cancel button in Safari 5 and Chrome on OS X. */
input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration {
    -webkit-appearance: none;
}

/** Remove inner padding and border in Firefox 4+. */
button::-moz-focus-inner, input::-moz-focus-inner {
    border: 0;
    padding: 0;
}

/** 1. Remove default vertical scrollbar in IE 8/9. 2. Improve readability and alignment in all browsers. */
textarea {
    overflow: auto; /* 1 */
    vertical-align: top; /* 2 */
}

/* ========================================================================== Tables ========================================================================== */
/** Remove most spacing between table cells. */
table {
    border-collapse: collapse;
    border-spacing: 0;
}

*, *:before, *:after {
    -moz-box-sizing: border-box;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
}

html, body {
    font-size: 100%;
}

body {
    background: white;
    color: var(--foreground-color);
    padding: 0;
    margin: 0;
    font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif;
    font-weight: normal;
    font-style: normal;
    line-height: 1;
    position: relative;
    cursor: auto;
}

a:hover {
    cursor: pointer;
}

a:focus {
    outline: none;
}

img, object, embed {
    max-width: 100%;
    height: auto;
}

object, embed {
    height: 100%;
}

img {
    -ms-interpolation-mode: bicubic;
}

#map_canvas img, #map_canvas embed, #map_canvas object, .map_canvas img, .map_canvas embed, .map_canvas object {
    max-width: none !important;
}

.left {
    float: left !important;
}

.right {
    float: right !important;
}

.text-left {
    text-align: left !important;
}

.text-right {
    text-align: right !important;
}

.text-center {
    text-align: center !important;
}

.text-justify {
    text-align: justify !important;
}

.hide {
    display: none;
}

.antialiased, body {
    -webkit-font-smoothing: antialiased;
}

img {
    display: inline-block;
    vertical-align: middle;
}

textarea {
    height: auto;
    min-height: 50px;
}

select {
    width: 100%;
}

p.lead, .paragraph.lead > p, #preamble > .sectionbody > .paragraph:first-of-type p {
    font-size: 1.21875em;
    line-height: 1.6;
}

.subheader, #content #toctitle, .admonitionblock td.content > .title, .exampleblock > .title, .imageblock > .title, .videoblock > .title, .listingblock > .title, .literalblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, .sidebarblock > .title, .tableblock > .title, .verseblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title, .tableblock > caption {
    line-height: 1.4;
    color: var(--accent-primary-color);
    font-weight: 300;
    margin-top: 0.5em;
    margin-bottom: 0.5em;
}

/* Typography resets */
div, dl, dt, dd, ul, ol, li, h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6, pre, form, p, blockquote, th, td {
    margin: 0;
    /*padding: 0;*/
    direction: ltr;
}

/* Default Link Styles */
a {
    color: var(--link-color-default);
    text-decoration: none;
    line-height: inherit;
}

a:hover, a:focus {
    color: var(--header-color);
}

a img {
    border: none;
}

/* Default paragraph styles */
p {
    font-family: inherit;
    font-weight: normal;
    font-size: 1em;
    line-height: 1.5;
    margin-bottom: 1.25em;
    text-rendering: optimizeLegibility;
}

p aside {
    font-size: 0.875em;
    line-height: 1.35;
    font-style: italic;
}

/* Default header styles */
h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 {
    font-family: ff-meta-web-pro-1, ff-meta-web-pro-2, Arial, "Helvetica Neue", sans-serif;
    font-weight: bold;
    font-style: normal;
    color: var(--accent-primary-color);
    text-rendering: optimizeLegibility;
    margin-top: 1em;
    margin-bottom: 0.5em;
    line-height: 1.2125em;
}

h1 small, h2 small, h3 small, #toctitle small, .sidebarblock > .content > .title small, h4 small, h5 small, h6 small {
    font-size: 60%;
    color: var(--accent-secondary-color);
    line-height: 0;
}

h1 {
    font-size: 2.125em;
}

h2 {
    font-size: 1.6875em;
}

h3, #toctitle, .sidebarblock > .content > .title {
    font-size: 1.375em;
}

h4 {
    font-size: 1.125em;
}

h5 {
    font-size: 1.125em;
}

h6 {
    font-size: 1em;
}

hr {
    border: solid var(--border2);
    border-width: 1px 0 0;
    clear: both;
    margin: 1.25em 0 1.1875em;
    height: 0;
}

/* Helpful Typography Defaults */
em, i {
    font-style: italic;
    line-height: inherit;
}

strong, b {
    font-weight: bold;
    line-height: inherit;
}

small {
    font-size: 60%;
    line-height: inherit;
}

code {
    font-family: "Consolas", "Deja Vu Sans Mono", "Bitstream Vera Sans Mono", monospace;
    font-weight: normal;
    color: var(--link-color-default);
}

/* Lists */
ul, ol, dl {
    font-size: 1em;
    line-height: 1.5;
    margin-bottom: 1.25em;
    list-style-position: outside;
    font-family: inherit;
}

ul, ol {
    margin-left: 0;
}

/* Unordered Lists */
ul li ul, ul li ol {
    margin-left: 1.25em;
    margin-bottom: 0;
    font-size: 1em; /* Override nested font-size change */
}

ul.square li ul, ul.circle li ul, ul.disc li ul {
    list-style: inherit;
}

ul.square {
    list-style-type: square;
}

ul.circle {
    list-style-type: circle;
}

ul.disc {
    list-style-type: disc;
}

ul.no-bullet {
    list-style: none;
}

/* Ordered Lists */
ol li ul, ol li ol {
    margin-left: 1.25em;
    margin-bottom: 0;
}

/* Definition Lists */
dl dt {
    margin-bottom: 0.3em;
    font-weight: bold;
}

dl dd {
    margin-bottom: 0.75em;
}

/* Abbreviations */
abbr, acronym {
    text-transform: uppercase;
    font-size: 90%;
    color: black;
    border-bottom: 1px dotted var(--border2);
    cursor: help;
}

abbr {
    text-transform: none;
}

/* Blockquotes */
blockquote {
    margin: 0 0 1.25em;
    padding: 0.5625em 1.25em 0 1.1875em;
    border-left: 1px solid var(--border2);
}

blockquote cite {
    display: block;
    font-size: 0.8125em;
    color: var(--block-quote-color);
}

blockquote cite:before {
    content: "\2014 \0020";
}

blockquote cite a, blockquote cite a:visited {
    color: var(--block-quote-color);
}

blockquote, blockquote p {
    line-height: 1.5;
    color: var(--accent-secondary-color);
}

/* Microformats */
.vcard {
    display: inline-block;
    margin: 0 0 1.25em 0;
    border: 1px solid var(--border2);
    padding: 0.625em 0.75em;
}

.vcard li {
    margin: 0;
    display: block;
}

.vcard .fn {
    font-weight: bold;
    font-size: 0.9375em;
}

.vevent .summary {
    font-weight: bold;
}

.vevent abbr {
    cursor: auto;
    text-decoration: none;
    font-weight: bold;
    border: none;
    padding: 0 0.0625em;
}

@media only screen and (min-width: 768px) {
    h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 {
        line-height: 1.4;
    }

    h1 {
        font-size: 2.75em;
    }

    h2 {
        font-size: 2.3125em;
    }

    h3, #toctitle, .sidebarblock > .content > .title {
        font-size: 1.6875em;
    }

    h4 {
        font-size: 1.4375em;
    }
}

/* Print styles.  Inlined to avoid required HTTP connection: www.phpied.com/delay-loading-your-print-css/ Credit to Paul Irish and HTML5 Boilerplate (html5boilerplate.com)
*/
.print-only {
    display: none !important;
}

@media print {
    * {
        background: transparent !important;
        color: var(--black-color) !important; /* Black prints faster: h5bp.com/s */
        box-shadow: none !important;
        text-shadow: none !important;
    }

    a, a:visited {
        text-decoration: underline;
    }

    a[href]:after {
        content: " (" attr(href) ")";
    }

    abbr[title]:after {
        content: " (" attr(title) ")";
    }

    .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after {
        content: "";
    }

    pre, blockquote {
        border: 1px solid #999;
        page-break-inside: avoid;
    }

    thead {
        display: table-header-group; /* h5bp.com/t */
    }

    tr, img {
        page-break-inside: avoid;
    }

    img {
        max-width: 100% !important;
    }

    @page {
        margin: 0.5cm;
    }

    p, h2, h3, #toctitle, .sidebarblock > .content > .title {
        orphans: 3;
        widows: 3;
    }

    h2, h3, #toctitle, .sidebarblock > .content > .title {
        page-break-after: avoid;
    }

    .hide-on-print {
        display: none !important;
    }

    .print-only {
        display: block !important;
    }

    .hide-for-print {
        display: none !important;
    }

    .show-for-print {
        display: inherit !important;
    }
}

/* Tables */
table {
    background: white;
    margin-bottom: 1.25em;
    border: solid 0 var(--border2);
}

table thead, table tfoot {
    background: none;
    font-weight: bold;
}

table thead tr th, table thead tr td, table tfoot tr th, table tfoot tr td {
    padding: 1px 8px 1px 5px;
    font-size: 1em;
    color: var(--foreground-color);
    text-align: left;
}

table tr th, table tr td {
    padding: 1px 8px 1px 5px;
    font-size: 1em;
    color: var(--foreground-color);
}

table tr.even, table tr.alt, table tr:nth-of-type(even) {
    background: none;
}

table thead tr th, table tfoot tr th, table tbody tr td, table tr td, table tfoot tr td {
    display: table-cell;
    line-height: 1.5;
}

.clearfix:before, .clearfix:after, .float-group:before, .float-group:after {
    content: " ";
    display: table;
}

.clearfix:after, .float-group:after {
    clear: both;
}

*:not(pre) > code {
    font-size: 0.95em;
    padding: 0;
    white-space: nowrap;
    background-color: var(--pre-background-color);
    border: 0 solid var(--border2);
    -webkit-border-radius: var(--border-radius-default);
    border-radius: var(--border-radius-default);
    text-shadow: none;
}

pre, pre > code {
    line-height: 1.2;
    color: inherit;
    font-family: "Consolas", "Deja Vu Sans Mono", "Bitstream Vera Sans Mono", monospace;
    font-weight: normal;
}

kbd.keyseq {
    color: var(--dark-gray-color);
}

kbd:not(.keyseq) {
    display: inline-block;
    color: black;
    font-size: 0.75em;
    line-height: 1.4;
    background-color: var(--kdb-background-color);
    border: 1px solid #ccc;
    -webkit-border-radius: 3px;
    border-radius: 3px;
    -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 2px white inset;
    box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 2px white inset;
    margin: -0.15em 0.15em 0 0.15em;
    padding: 0.2em 0.6em 0.2em 0.5em;
    vertical-align: middle;
    white-space: nowrap;
}

kbd kbd:first-child {
    margin-left: 0;
}

kbd kbd:last-child {
    margin-right: 0;
}

.menuseq, .menu {
    color: black;
}

p a > code:hover {
    color: var(--link-hover-color);
}

#header, #content, #footnotes, #footer {
    width: 100%;
    margin-left: auto;
    margin-right: auto;
    margin-top: 0;
    margin-bottom: 0;
    max-width: 62.5em;
    *zoom: 1;
    position: relative;
    padding-left: 0.9375em;
    padding-right: 0.9375em;
}

#header:before, #header:after, #content:before, #content:after, #footnotes:before, #footnotes:after, #footer:before, #footer:after {
    content: " ";
    display: table;
}

#header:after, #content:after, #footnotes:after, #footer:after {
    clear: both;
}

#header {
    margin-bottom: 2.5em;
}

#header > h1 {
    color: var(--header-color);
    font-weight: bold;
    border-bottom: 1px solid var(--border2);
    margin-bottom: -28px;
    padding-bottom: 32px;
}

#header span {
    color: var(--accent-secondary-color);
}

#header #revnumber {
    text-transform: capitalize;
}

#header br {
    display: none;
}

#header br + span {
    padding-left: 3px;
}

#header br + span:before {
    content: "\2013 \0020";
}

#header br + span.author {
    padding-left: 0;
}

#header br + span.author:before {
    content: ", ";
}

#toc {
    border-bottom: 1px solid var(--border2);
    padding-bottom: 1.25em;
}

#toc > ul {
    margin-left: 0.25em;
}

#toc ul.sectlevel0 > li > a {
    font-style: italic;
}

#toc ul.sectlevel0 ul.sectlevel1 {
    margin-left: 0;
    margin-top: 0.5em;
    margin-bottom: 0.5em;
}

#toc ul {
    list-style-type: none;
}

#toctitle {
    color: var(--accent-primary-color);
}

@media only screen and (min-width: 1280px) {
    body.toc2 {
        padding-left: 20em;
    }

    #toc.toc2 {
        position: fixed;
        width: 20em;
        left: 0;
        top: 0;
        border-right: 1px solid var(--border2);
        border-bottom: 0;
        z-index: 1000;
        padding: 44px 1em 1em;
        height: 100%;
        overflow: auto;
    }

    #toc.toc2 > ul {
        padding-left: 1em;
    }

    #toc.toc2 #toctitle {
        margin-top: 0;
    }

    #toc.toc2 > ul {
        font-size: .95em;
    }

    #toc.toc2 ul ul {
        margin-left: 0;
        padding-left: 1.25em;
    }

    #toc.toc2 ul.sectlevel0 ul.sectlevel1 {
        padding-left: 0;
        margin-top: 0.5em;
        margin-bottom: 0.5em;
    }

    body.toc2.toc-right {
        padding-left: 0;
        padding-right: 20em;
    }

    body.toc2.toc-right #toc.toc2 {
        border-right: 0;
        border-left: 1px solid var(--border2);
        left: auto;
        right: 0;
    }
}

#content #toc {
    border-style: solid;
    border-width: 1px;
    border-color: var(--toc-border-color);
    margin-bottom: 1.25em;
    padding: 1.25em;
    background: var(--pre-background-color);
    border-width: 0;
    -webkit-border-radius: var(--border-radius-default);
    border-radius: var(--border-radius-default);
}

#content #toc > :first-child {
    margin-top: 0;
}

#content #toc > :last-child {
    margin-bottom: 0;
}

#content #toc a {
    color: var(--gray-color);
    text-decoration: none;
}

#content #toctitle {
    font-weight: bold;
    font-family: ff-meta-web-pro-1, ff-meta-web-pro-2, Arial, "Helvetica Neue", sans-serif;
    font-size: 1em;
    padding-left: 0.125em;
}

#footer {
    max-width: 100%;
    background-color: black;
    padding: 1.25em;
}

#footer-text {
    color: white;
    line-height: 1.35;
}

.sect1 {
    padding-bottom: 1.25em;
}

.sect1 + .sect1 {
    border-top: 1px solid var(--border2);
}

#content h1 > a.anchor, h2 > a.anchor, h3 > a.anchor, #toctitle > a.anchor, .sidebarblock > .content > .title > a.anchor, h4 > a.anchor, h5 > a.anchor, h6 > a.anchor {
    position: absolute;
    width: 1em;
    margin-left: -1em;
    display: block;
    text-decoration: none;
    visibility: hidden;
    text-align: center;
    font-weight: normal;
}

#content h1 > a.anchor:before, h2 > a.anchor:before, h3 > a.anchor:before, #toctitle > a.anchor:before, .sidebarblock > .content > .title > a.anchor:before, h4 > a.anchor:before, h5 > a.anchor:before, h6 > a.anchor:before {
    content: '\00A7';
    font-size: .85em;
    vertical-align: text-top;
    display: block;
    margin-top: 0.05em;
}

#content h1:hover > a.anchor, #content h1 > a.anchor:hover, h2:hover > a.anchor, h2 > a.anchor:hover, h3:hover > a.anchor, #toctitle:hover > a.anchor, .sidebarblock > .content > .title:hover > a.anchor, h3 > a.anchor:hover, #toctitle > a.anchor:hover, .sidebarblock > .content > .title > a.anchor:hover, h4:hover > a.anchor, h4 > a.anchor:hover, h5:hover > a.anchor, h5 > a.anchor:hover, h6:hover > a.anchor, h6 > a.anchor:hover {
    visibility: visible;
}

#content h1 > a.link, h2 > a.link, h3 > a.link, #toctitle > a.link, .sidebarblock > .content > .title > a.link, h4 > a.link, h5 > a.link, h6 > a.link {
    color: var(--accent-primary-color);
    text-decoration: none;
}

#content h1 > a.link:hover, h2 > a.link:hover, h3 > a.link:hover, #toctitle > a.link:hover, .sidebarblock > .content > .title > a.link:hover, h4 > a.link:hover, h5 > a.link:hover, h6 > a.link:hover {
    color: var(--link-hover-color);
}

.imageblock, .literalblock, .listingblock, .verseblock, .videoblock {
    margin-bottom: 1.25em;
}

.admonitionblock td.content > .title, .exampleblock > .title, .imageblock > .title, .videoblock > .title, .listingblock > .title, .literalblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, .sidebarblock > .title, .tableblock > .title, .verseblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title {
    text-align: left;
    font-weight: bold;
}

.tableblock > caption {
    text-align: left;
    font-weight: bold;
    white-space: nowrap;
    overflow: visible;
    max-width: 0;
}

table.tableblock #preamble > .sectionbody > .paragraph:first-of-type p {
    font-size: inherit;
}

.admonitionblock > table {
    border: 0;
    background: none;
    width: 100%;
}

.admonitionblock > table td.icon {
    text-align: center;
    width: 60px;
    padding: 0;
    font-size: 1.5em;
}

.admonitionblock > table td.icon img {
    max-width: none;
}

.admonitionblock > table td.icon .title {
    font-weight: bold;
    text-transform: uppercase;
}

.admonitionblock > table td.content {
    /*padding-left: 1.125em;*/
    /*padding-right: 1.25em;*/
    border-left: 1px solid var(--border2);
    color: var(--accent-secondary-color);
}

.admonitionblock > table td.content > :last-child > :last-child {
    margin-bottom: 0;
}

.exampleblock > .content {
    border-style: solid;
    border-width: 1px;
    border-color: var(--exampleblock-border-color);
    margin-bottom: 1.25em;
    padding: 1.25em;
    background: white;
    -webkit-border-radius: var(--border-radius-default);
    border-radius: var(--border-radius-default);
}

.exampleblock > .content > :first-child {
    margin-top: 0;
}

.exampleblock > .content > :last-child {
    margin-bottom: 0;
}

.exampleblock > .content h1, .exampleblock > .content h2, .exampleblock > .content h3, .exampleblock > .content #toctitle, .sidebarblock.exampleblock > .content > .title, .exampleblock > .content h4, .exampleblock > .content h5, .exampleblock > .content h6, .exampleblock > .content p {
    color: var(--dark-gray-color);
}

.exampleblock > .content h1, .exampleblock > .content h2, .exampleblock > .content h3, .exampleblock > .content #toctitle, .sidebarblock.exampleblock > .content > .title, .exampleblock > .content h4, .exampleblock > .content h5, .exampleblock > .content h6 {
    line-height: 1;
    margin-bottom: 0.625em;
}

.exampleblock > .content h1.subheader, .exampleblock > .content h2.subheader, .exampleblock > .content h3.subheader, .exampleblock > .content .subheader#toctitle, .sidebarblock.exampleblock > .content > .subheader.title, .exampleblock > .content h4.subheader, .exampleblock > .content h5.subheader, .exampleblock > .content h6.subheader {
    line-height: 1.4;
}

.exampleblock.result > .content {
    -webkit-box-shadow: 0 1px 8px var(--toc-border-color);
    box-shadow: 0 1px 8px var(--toc-border-color);
}

.sidebarblock {
    border-style: solid;
    border-width: 1px;
    border-color: var(--toc-border-color);
    margin-bottom: 1.25em;
    padding: 1.25em;
    background: var(--pre-background-color);
    -webkit-border-radius: var(--border-radius-default);
    border-radius: var(--border-radius-default);
}

.sidebarblock > :first-child {
    margin-top: 0;
}

.sidebarblock > :last-child {
    margin-bottom: 0;
}

.sidebarblock h1, .sidebarblock h2, .sidebarblock h3, .sidebarblock #toctitle, .sidebarblock > .content > .title, .sidebarblock h4, .sidebarblock h5, .sidebarblock h6, .sidebarblock p {
    color: var(--dark-gray-color);
}

.sidebarblock h1, .sidebarblock h2, .sidebarblock h3, .sidebarblock #toctitle, .sidebarblock > .content > .title, .sidebarblock h4, .sidebarblock h5, .sidebarblock h6 {
    line-height: 1;
    margin-bottom: 0.625em;
}

.sidebarblock h1.subheader, .sidebarblock h2.subheader, .sidebarblock h3.subheader, .sidebarblock .subheader#toctitle, .sidebarblock > .content > .subheader.title, .sidebarblock h4.subheader, .sidebarblock h5.subheader, .sidebarblock h6.subheader {
    line-height: 1.4;
}

.sidebarblock > .content > .title {
    color: var(--gray-color);
    margin-top: 0;
    line-height: 1.5;
}

.exampleblock > .content > :last-child > :last-child, .exampleblock > .content .olist > ol > li:last-child > :last-child, .exampleblock > .content .ulist > ul > li:last-child > :last-child, .exampleblock > .content .qlist > ol > li:last-child > :last-child, .sidebarblock > .content > :last-child > :last-child, .sidebarblock > .content .olist > ol > li:last-child > :last-child, .sidebarblock > .content .ulist > ul > li:last-child > :last-child, .sidebarblock > .content .qlist > ol > li:last-child > :last-child {
    margin-bottom: 0;
}

.literalblock > .content pre, .listingblock > .content pre {
    background: #eeeeee;
    border-width: 1px;
    border-style: solid;
    border-color: #cccccc;
    -webkit-border-radius: var(--border-radius-default);
    border-radius: var(--border-radius-default);
    padding: 0.5em;
    word-wrap: break-word;
}

.literalblock > .content pre.nowrap, .listingblock > .content pre.nowrap {
    overflow-x: auto;
    white-space: pre;
    word-wrap: normal;
}

.literalblock > .content pre > code, .listingblock > .content pre > code {
    display: block;
}

@media only screen {
    .literalblock > .content pre, .listingblock > .content pre {
        font-size: 0.76em;
    }
}

@media only screen and (min-width: 768px) {
    .literalblock > .content pre, .listingblock > .content pre {
        font-size: 0.855em;
    }
}

@media only screen and (min-width: 1280px) {
    .literalblock > .content pre, .listingblock > .content pre {
        font-size: 0.95em;
    }
}

.listingblock > .content {
    position: relative;
}

.listingblock:hover code[class*=" language-"]:before {
    text-transform: uppercase;
    font-size: 0.9em;
    color: #999;
    position: absolute;
    top: 0.375em;
    right: 0.375em;
}

.listingblock:hover code.asciidoc:before {
    content: "asciidoc";
}

.listingblock:hover code.clojure:before {
    content: "clojure";
}

.listingblock:hover code.css:before {
    content: "css";
}

.listingblock:hover code.groovy:before {
    content: "groovy";
}

.listingblock:hover code.html:before {
    content: "html";
}

.listingblock:hover code.java:before {
    content: "java";
}

.listingblock:hover code.javascript:before {
    content: "javascript";
}

.listingblock:hover code.python:before {
    content: "python";
}

.listingblock:hover code.ruby:before {
    content: "ruby";
}

.listingblock:hover code.scss:before {
    content: "scss";
}

.listingblock:hover code.xml:before {
    content: "xml";
}

.listingblock:hover code.yaml:before {
    content: "yaml";
}

.listingblock.terminal pre .command:before {
    content: attr(data-prompt);
    padding-right: 0.5em;
    color: #999;
}

.listingblock.terminal pre .command:not([data-prompt]):before {
    content: '$';
}

table.pyhltable {
    border: 0;
    margin-bottom: 0;
}

table.pyhltable td {
    vertical-align: top;
    padding-top: 0;
    padding-bottom: 0;
}

table.pyhltable td.code {
    padding-left: .75em;
    padding-right: 0;
}

.highlight.pygments .lineno, table.pyhltable td:not(.code) {
    color: #999;
    padding-left: 0;
    padding-right: .5em;
    border-right: 1px solid var(--border2);
}

.highlight.pygments .lineno {
    display: inline-block;
    margin-right: .25em;
}

table.pyhltable .linenodiv {
    background-color: transparent !important;
    padding-right: 0 !important;
}

.quoteblock {
    margin: 0 0 1.25em;
    padding: 0.5625em 1.25em 0 1.1875em;
    border-left: 1px solid var(--border2);
}

.quoteblock blockquote {
    margin: 0 0 1.25em 0;
    padding: 0 0 0.5625em 0;
    border: 0;
}

.quoteblock blockquote > .paragraph:last-child p {
    margin-bottom: 0;
}

.quoteblock .attribution {
    margin-top: -.25em;
    padding-bottom: 0.5625em;
    font-size: 0.8125em;
    color: var(--block-quote-color);
}

.quoteblock .attribution br {
    display: none;
}

.quoteblock .attribution cite {
    display: block;
    margin-bottom: 0.625em;
}

table thead th, table tfoot th {
    font-weight: bold;
}

table.tableblock.grid-all {
    border-collapse: separate;
    border-spacing: 1px;
    -webkit-border-radius: var(--border-radius-default);
    border-radius: var(--border-radius-default);
    border-top: 0 solid var(--border2);
    border-bottom: 0 solid var(--border2);
}

table.tableblock.frame-topbot, table.tableblock.frame-none {
    border-left: 0;
    border-right: 0;
}

table.tableblock.frame-sides, table.tableblock.frame-none {
    border-top: 0;
    border-bottom: 0;
}

table.tableblock td .paragraph:last-child p, table.tableblock td > p:last-child {
    margin-bottom: 0;
}

th.tableblock.halign-left, td.tableblock.halign-left {
    text-align: left;
}

th.tableblock.halign-right, td.tableblock.halign-right {
    text-align: right;
}

th.tableblock.halign-center, td.tableblock.halign-center {
    text-align: center;
}

th.tableblock.valign-top, td.tableblock.valign-top {
    vertical-align: top;
}

th.tableblock.valign-bottom, td.tableblock.valign-bottom {
    vertical-align: bottom;
}

th.tableblock.valign-middle, td.tableblock.valign-middle {
    vertical-align: middle;
}

p.tableblock.header {
    color: var(--foreground-color);
    font-weight: bold;
}

td > div.verse {
    white-space: pre;
}

ol {
    margin-left: 0.25em;
}

ul li ol {
    margin-left: 0;
}

dl dd {
    margin-left: 1.125em;
}

dl dd:last-child, dl dd:last-child > :last-child {
    margin-bottom: 0;
}

ol > li p, ul > li p, ul dd, ol dd, .olist .olist, .ulist .ulist, .ulist .olist, .olist .ulist {
    margin-bottom: 0.625em;
}

ul.unstyled, ol.unnumbered, ul.checklist, ul.none {
    list-style-type: none;
}

ul.unstyled, ol.unnumbered, ul.checklist {
    margin-left: 0.625em;
}

ul.checklist li > p:first-child > i[class^="icon-check"]:first-child, ul.checklist li > p:first-child > input[type="checkbox"]:first-child {
    margin-right: 0.25em;
}

ul.checklist li > p:first-child > input[type="checkbox"]:first-child {
    position: relative;
    top: 1px;
}

ul.inline {
    margin: 0 auto 0.625em auto;
    margin-left: -1.375em;
    margin-right: 0;
    padding: 0;
    list-style: none;
    overflow: hidden;
}

ul.inline > li {
    list-style: none;
    float: left;
    margin-left: 1.375em;
    display: block;
}

ul.inline > li > * {
    display: block;
}

.unstyled dl dt {
    font-weight: normal;
    font-style: normal;
}

ol.arabic {
    list-style-type: decimal;
}

ol.decimal {
    list-style-type: decimal-leading-zero;
}

ol.loweralpha {
    list-style-type: lower-alpha;
}

ol.upperalpha {
    list-style-type: upper-alpha;
}

ol.lowerroman {
    list-style-type: lower-roman;
}

ol.upperroman {
    list-style-type: upper-roman;
}

ol.lowergreek {
    list-style-type: lower-greek;
}

.hdlist > table, .colist > table {
    border: 0;
    background: none;
}

.hdlist > table > tbody > tr, .colist > table > tbody > tr {
    background: none;
}

td.hdlist1 {
    padding-right: .8em;
    font-weight: bold;
}

td.hdlist1, td.hdlist2 {
    vertical-align: top;
}

.literalblock + .colist, .listingblock + .colist {
    margin-top: -0.5em;
}

.colist > table tr > td:first-of-type {
    padding: 0 .8em;
    line-height: 1;
}

.colist > table tr > td:last-of-type {
    padding: 0.25em 0;
}

.qanda > ol > li > p > em:only-child {
    color: var(--em-color);
}

.thumb, .th {
    line-height: 0;
    display: inline-block;
    border: solid 4px white;
    -webkit-box-shadow: 0 0 0 1px var(--border2);
    box-shadow: 0 0 0 1px var(--border2);
}

.imageblock.left, .imageblock[style*="float: left"] {
    margin: 0.25em 0.625em 1.25em 0;
}

.imageblock.right, .imageblock[style*="float: right"] {
    margin: 0.25em 0 1.25em 0.625em;
}

.imageblock > .title {
    margin-bottom: 0;
}

.imageblock.thumb, .imageblock.th {
    border-width: var(--border-radius-default);
}

.imageblock.thumb > .title, .imageblock.th > .title {
    padding: 0 0.125em;
}

.image.left, .image.right {
    margin-top: 0.25em;
    margin-bottom: 0.25em;
    display: inline-block;
    line-height: 0;
}

.image.left {
    margin-right: 0.625em;
}

.image.right {
    margin-left: 0.625em;
}

a.image {
    text-decoration: none;
}

span.footnote, span.footnoteref {
    vertical-align: super;
    font-size: 0.875em;
}

span.footnote a, span.footnoteref a {
    text-decoration: none;
}

#footnotes {
    padding-top: 0.75em;
    padding-bottom: 0.75em;
    margin-bottom: 0.625em;
}

#footnotes hr {
    width: 20%;
    min-width: 6.25em;
    margin: -.25em 0 .75em 0;
    border-width: 1px 0 0 0;
}

#footnotes .footnote {
    padding: 0 0.375em;
    line-height: 1.3;
    font-size: 0.875em;
    margin-left: 1.2em;
    text-indent: -1.2em;
    margin-bottom: .2em;
}

#footnotes .footnote a:first-of-type {
    font-weight: bold;
    text-decoration: none;
}

#footnotes .footnote:last-of-type {
    margin-bottom: 0;
}

#content #footnotes {
    margin-top: -0.625em;
    margin-bottom: 0;
    padding: 0.75em 0;
}

.gist .file-data > table {
    border: none;
    background: var(--background);
    width: 100%;
    margin-bottom: 0;
}

.gist .file-data > table td.line-data {
    width: 99%;
}

div.unbreakable {
    page-break-inside: avoid;
}

.big {
    font-size: larger;
}

.small {
    font-size: smaller;
}

.underline {
    text-decoration: underline;
}

.overline {
    text-decoration: overline;
}

.line-through {
    text-decoration: line-through;
}

.aqua {
    color: #00bfbf;
}

.aqua-background {
    background-color: #00fafa;
}

.black {
    color: black;
}

.black-background {
    background-color: black;
}

.blue {
    color: #0000bf;
}

.blue-background {
    background-color: #0000fa;
}

.fuchsia {
    color: #bf00bf;
}

.fuchsia-background {
    background-color: #fa00fa;
}

.gray {
    color: #606060;
}

.gray-background {
    background-color: #7d7d7d;
}

.green {
    color: #006000;
}

.green-background {
    background-color: #007d00;
}

.lime {
    color: #00bf00;
}

.lime-background {
    background-color: #00fa00;
}

.maroon {
    color: #600000;
}

.maroon-background {
    background-color: #7d0000;
}

.navy {
    color: #000060;
}

.navy-background {
    background-color: #00007d;
}

.olive {
    color: #606000;
}

.olive-background {
    background-color: #7d7d00;
}

.purple {
    color: #600060;
}

.purple-background {
    background-color: #7d007d;
}

.red {
    color: #bf0000;
}

.red-background {
    background-color: #fa0000;
}

.silver {
    color: #909090;
}

.silver-background {
    background-color: #bcbcbc;
}

.teal {
    color: #006060;
}

.teal-background {
    background-color: #007d7d;
}

.white {
    color: #bfbfbf;
}

.white-background {
    background-color: #fafafa;
}

.yellow {
    color: #bfbf00;
}

.yellow-background {
    background-color: #fafa00;
}

span.icon > [class^="icon-"], span.icon > [class*=" icon-"] {
    cursor: default;
}


.admonitionblock td.icon [class^="icon-"]:before {
    font-size: 2.5em;
    text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
    cursor: default;
}

.admonitionblock td.icon .icon-note:before {
    content: "\f05a";
    color: var(--accent-primary-color);
}

.admonitionblock td.icon .icon-tip:before {
    content: "\f0eb";
    /*text-shadow: 1px 1px 2px var(--accent-primary-color);*/
    color: var(--accent-primary-color);
}

.fa {
    font-family: "FontAwesome", "Helvetica Neue", sans-serif;
}

.admonitionblock td.icon .icon-warning:before {
    content: "\f071";
    color: #bf6900;
}

.admonitionblock td.icon .icon-caution:before {
    content: "\f06d";
    color: #bf3400;
}

.admonitionblock td.icon .icon-important:before {
    content: "\f06a";
    color: #bf0000;
}

.conum {
    display: inline-block;
    color: white !important;
    background-color: black;
    -webkit-border-radius: 100px;
    border-radius: 100px;
    text-align: center;
    width: 20px;
    height: 20px;
    font-size: 12px;
    font-weight: bold;
    line-height: 20px;
    font-family: Arial, sans-serif;
    font-style: normal;
    position: relative;
    top: -2px;
    letter-spacing: -1px;
}

.conum * {
    color: white !important;
}

.conum + b {
    display: none;
}

.conum:after {
    content: attr(data-value);
}

.conum:not([data-value]):empty {
    display: none;
}

h4 {
    color: var(--gray-color);
}

.literalblock > .content > pre, .listingblock > .content > pre {
    -webkit-border-radius: var(--border-radius-default);
    border-radius: var(--border-radius-default);
    margin-left: 0.5em;
    margin-right: 0.5em;
}

.admonitionblock {
    margin-left: .5em;
    margin-right: .5em;
}

.admonitionblock > table {
    border: 1px solid var(--accent-primary-color);
    border-left-width: 4px;
    background-color: var(--admotion-background-color);
    border-collapse: separate;
    -webkit-border-radius: var(--border-radius-default);
    border-radius: var(--border-radius-default);
}

.admonitionblock > table td.icon {
    padding-top: .5em;
    padding-bottom: .5em;
}

.admonitionblock > table td.content {
    padding: .5em .2em;
    color: black;
    font-size: .9em;
    border-left: none;
}

.sidebarblock {
    background-color: var(--sidebar-background-color);
    border-color: #ccc;
}

.sidebarblock > .content > .title {
    color: var(--link-color-default);
}

table.tableblock.grid-all {
    border-collapse: collapse;
    -webkit-border-radius: 0;
    border-radius: 0;
}

table.tableblock.grid-all th.tableblock, table.tableblock.grid-all td.tableblock {
    border-bottom: 1px solid #aaa;
}

#footer {
    background-color: var(--accent-primary-color);
    padding: 2em;
}

#footer-text {
    color: #eee;
    font-size: 0.8em;
    text-align: center;
}
</style>
<style>
pre.rouge table td { padding: 5px; }
pre.rouge table pre { margin: 0; }
pre.rouge, pre.rouge .w {
  color: #24292f;
  background-color: #f6f8fa;
}
pre.rouge .k, pre.rouge .kd, pre.rouge .kn, pre.rouge .kp, pre.rouge .kr, pre.rouge .kt, pre.rouge .kv {
  color: #cf222e;
}
pre.rouge .gr {
  color: #f6f8fa;
}
pre.rouge .gd {
  color: #82071e;
  background-color: #ffebe9;
}
pre.rouge .nb {
  color: #953800;
}
pre.rouge .nc {
  color: #953800;
}
pre.rouge .no {
  color: #953800;
}
pre.rouge .nn {
  color: #953800;
}
pre.rouge .sr {
  color: #116329;
}
pre.rouge .na {
  color: #116329;
}
pre.rouge .nt {
  color: #116329;
}
pre.rouge .gi {
  color: #116329;
  background-color: #dafbe1;
}
pre.rouge .ges {
  font-weight: bold;
  font-style: italic;
}
pre.rouge .kc {
  color: #0550ae;
}
pre.rouge .l, pre.rouge .ld, pre.rouge .m, pre.rouge .mb, pre.rouge .mf, pre.rouge .mh, pre.rouge .mi, pre.rouge .il, pre.rouge .mo, pre.rouge .mx {
  color: #0550ae;
}
pre.rouge .sb {
  color: #0550ae;
}
pre.rouge .bp {
  color: #0550ae;
}
pre.rouge .ne {
  color: #0550ae;
}
pre.rouge .nl {
  color: #0550ae;
}
pre.rouge .py {
  color: #0550ae;
}
pre.rouge .nv, pre.rouge .vc, pre.rouge .vg, pre.rouge .vi, pre.rouge .vm {
  color: #0550ae;
}
pre.rouge .o, pre.rouge .ow {
  color: #0550ae;
}
pre.rouge .gh {
  color: #0550ae;
  font-weight: bold;
}
pre.rouge .gu {
  color: #0550ae;
  font-weight: bold;
}
pre.rouge .s, pre.rouge .sa, pre.rouge .sc, pre.rouge .dl, pre.rouge .sd, pre.rouge .s2, pre.rouge .se, pre.rouge .sh, pre.rouge .sx, pre.rouge .s1, pre.rouge .ss {
  color: #0a3069;
}
pre.rouge .nd {
  color: #8250df;
}
pre.rouge .nf, pre.rouge .fm {
  color: #8250df;
}
pre.rouge .err {
  color: #f6f8fa;
  background-color: #82071e;
}
pre.rouge .c, pre.rouge .ch, pre.rouge .cd, pre.rouge .cm, pre.rouge .cp, pre.rouge .cpf, pre.rouge .c1, pre.rouge .cs {
  color: #6e7781;
}
pre.rouge .gl {
  color: #6e7781;
}
pre.rouge .gt {
  color: #6e7781;
}
pre.rouge .ni {
  color: #24292f;
}
pre.rouge .si {
  color: #24292f;
}
pre.rouge .ge {
  color: #24292f;
  font-style: italic;
}
pre.rouge .gs {
  color: #24292f;
  font-weight: bold;
}
</style>
</head>
<body class="article toc2 toc-left">
<div id="header">
<h1>RatingControl Sample</h1>
<div id="toc" class="toc2">
<div id="toctitle">Table of Contents</div>
<ul class="sectlevel1">
<li><a href="#_before_we_start">Before we start</a>
<ul class="sectlevel2">
<li><a href="#_some_basics_about_controls_in_avalonia">Some basics about Controls in Avalonia</a></li>
<li><a href="#_properties">Properties</a></li>
<li><a href="#_controltemplates">ControlTemplates</a></li>
<li><a href="#_the_concept_of_the_rating_control">The concept of the Rating-Control</a></li>
</ul>
</li>
<li><a href="#_the_solution">The Solution</a>
<ul class="sectlevel2">
<li><a href="#_step_1_create_a_new_project">Step 1: Create a new Project</a></li>
<li><a href="#_step_2_add_the_ratingcontrol_class">Step 2: Add the RatingControl-class</a></li>
<li><a href="#_step_3_add_the_numberofstars_property">Step 3: Add the NumberOfStars-Property</a></li>
<li><a href="#_step_4_add_the_value_property">Step 4: Add the Value-Property</a></li>
<li><a href="#_step_5_add_the_stars_property">Step 5: Add the Stars-Property</a></li>
<li><a href="#_step_6_update_the_stars_property">Step 6: Update the Stars-Property</a></li>
<li><a href="#_step_7_add_user_interaction">Step 7: Add user interaction</a></li>
<li><a href="#_step_8_add_a_controltheme_for_the_ratingcontrol">Step 8: Add a <code>ControlTheme</code> for the RatingControl</a></li>
<li><a href="#_step_9_theme_variant">Step 9: Theme Variant</a></li>
<li><a href="#_step_10_create_a_sample_to_try_out_the_custom_control">Step 10: Create a sample to try-out the custom Control</a></li>
<li><a href="#_step_11_see_it_in_action">Step 11: See it in action</a></li>
</ul>
</li>
<li><a href="#_related">Related</a></li>
</ul>
</div>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>This example will show you how to create a custom control. Our goal is to create a rating control, where the user can vote via clicking one of several stars.</p>
</div>
<!-- toc disabled -->
<h3 id="_difficulty" class="discrete">Difficulty</h3>
<div class="paragraph">
<p>🐔 Normal 🐔</p>
</div>
<h3 id="_buzz_words" class="discrete">Buzz-Words</h3>
<div class="paragraph">
<p>Control, TemplatedControl, custom Control, reusable Control, AvaloniaProperty, StyledProperty, DirectProperty, ReadonlyProperty, Style, ControlTheme</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_before_we_start">Before we start</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This sample assumes that you know the basics of Avalonia and the MVVM pattern.</p>
</div>
<div class="sect2">
<h3 id="_some_basics_about_controls_in_avalonia">Some basics about Controls in Avalonia</h3>
<div class="paragraph">
<p>Whenever we want to create a new control, we need to derive from a valid base class, which implements the needed interfaces and functions like styling, layout and user interaction. Below you can find a list of possible base-classes:</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1">Control</dt>
<dd>
<p>Use this as a base class if you want to render the control on your own and you want the control to look the same in every App (for example: <code>TextBlock</code>, <code>Image</code>).</p>
</dd>
<dt class="hdlist1">TemplatedControl</dt>
<dd>
<p>Use this control as your base class if you want to have a "lookless" control, which can be re-styled in any App.</p>
</dd>
<dt class="hdlist1">ContentControl</dt>
<dd>
<p>This control inherits <code>TemplatedControl</code>, but adds the ability to place any content inside.</p>
</dd>
</dl>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
You can also extend existing controls like <code>Button</code>, <code>TextBox</code> or <code>CheckBox</code>, if you want to use their functionality but adding your own logic on top.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>For more information about the types of controls please visit the <a href="https://docs.avaloniaui.net/docs/basics/user-interface/controls/creating-controls/choosing-a-custom-control-type">[Documentation]</a>.</p>
</div>
</div>
<div class="sect2">
<h3 id="_properties">Properties</h3>
<div class="paragraph">
<p>Avalonia has a property system which is responsible for storing and receiving the current value. The property system will take care of the styling, binding, validation and many more. There are three types of <code>AvaloniaProperties</code>. When you want to register your own property, you need to decide which property type is the right one for your use-case:</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1">StyledProperty</dt>
<dd>
<p>A <code>StyledProperty</code> is a property, which support styling and animation. use this type of property if you think the user of your control will most likely want to define this property in a <code>Style</code> (e.g.: <code>Foreground</code>, <code>Background</code>, <code>Margin</code>, &#8230;&#8203;).</p>
</dd>
<dt class="hdlist1">DirectProperty</dt>
<dd>
<p>A <code>DirectProperty</code> is a property which can only be read and set in an actual control instance. Use this property if it&#8217;s likely to be set individually on each instance (e.g.: <code>Text</code>, <code>Value</code>, &#8230;&#8203;) or if you need a read-only property (e.g:: <code>HasFocus</code>)</p>
</dd>
<dt class="hdlist1">AttachedProperty</dt>
<dd>
<p>An <code>AttachedProperty</code> is a property that can be set on any <code>Control</code>, even it doesn&#8217;t define the property on it&#8217;s own (e.g.: <code>Grid.Row</code>, <code>DockPanel.Dock</code>, &#8230;&#8203;)</p>
</dd>
</dl>
</div>
<div class="paragraph">
<p>If you want to learn more about <code>AvaloniaProperties</code> please visit the <a href="https://docs.avaloniaui.net/docs/basics/user-interface/controls/creating-controls/defining-properties">[Documentation]</a></p>
</div>
</div>
<div class="sect2">
<h3 id="_controltemplates">ControlTemplates</h3>
<div class="paragraph">
<p>In Avalonia <code>TemplatedControls</code> are lookless, which means they are not drawn by the <code>Control</code> itself. Instead, the developer needs to provide a <code>ControlTemplate</code> which is similar to a <code>DataTemplate</code>, but for the <code>Control</code>.</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1">TemplateParts</dt>
<dd>
<p>Some <code>Controls</code> require specific <code>Controls</code> inside the the <code>ControlTemplate</code>, which also needs to have a defined name in order to reference them inside the code behind. By convention these controls have a name with the prefix <code>"PART_"</code>. For example, you can use this to listen to events of these template parts.</p>
</dd>
<dt class="hdlist1">TemplateBinding</dt>
<dd>
<p>Inside <code>ControlTemplates</code> you can make use of a special <code>Binding</code> called <code>TemplateBinding</code>. To learn more about them, please visit the <a href="https://docs.avaloniaui.net/docs/guides/custom-controls/how-to-create-templated-controls#data-binding">[Documentation]</a>.</p>
</dd>
</dl>
</div>
</div>
<div class="sect2">
<h3 id="_the_concept_of_the_rating_control">The concept of the Rating-Control</h3>
<div class="paragraph">
<p>Before you create any control, you should already have an idea which functions it should provide, how it should look like and how the user should interact with the control. Remember, the user interface is the door to your program.</p>
</div>
<div class="paragraph">
<p>First of all we will write a list of requirements:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>The developer should be able to define the number of stars. Ideally this should be reusable via <code>Styles</code></p>
</li>
<li>
<p>The user must be able to select their rating</p>
</li>
<li>
<p>There must be a visual feedback showing the current rating</p>
</li>
<li>
<p>The user should be able to interact with the mouse</p>
</li>
<li>
<p>The user should be able to interact via touch</p>
</li>
<li>
<p>The user should be able to interact via keyboard</p>
</li>
<li>
<p>The developer should be able to validate the input</p>
</li>
<li>
<p>Any validation error should be shown to the user</p>
</li>
<li>
<p>The visual appearance should be easy to adjust</p>
</li>
</ul>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Items listed above containing the word <em>should</em> mean are nice to have but the control will also work without this requirement fulfilled. On the other hand, requirements with the word <strong>must</strong> cannot be omitted, as the function of the control will not be given.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Now that we know the functions we want to serve, we can create a simple sketch of how the control should look like:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="_docs/Sketch.png" alt="Sketch">
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_solution">The Solution</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_step_1_create_a_new_project">Step 1: Create a new Project</h3>
<div class="paragraph">
<p>In our sample we will create a new project using the <code>Avalonia MVVM Template</code>. We will place the sample App and the control together in the same project. In your real-world App you may want to create a <a href="https://learn.microsoft.com/en-us/dotnet/standard/class-library-overview">[<code>class library</code>-project]</a> for your custom controls, so they can be reused in several Apps.</p>
</div>
</div>
<div class="sect2">
<h3 id="_step_2_add_the_ratingcontrol_class">Step 2: Add the RatingControl-class</h3>
<div class="paragraph">
<p>In our project we create a new folder called <code>Controls</code>. inside this folder we will add a file called <code>RatingControl.cs</code>. Now we need to decide, which base-class we want to use. We want our control to be able to be re-styled by the developer, so we decide to base on <code>TemplatedControl</code>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cs"><span class="k">public</span> <span class="k">class</span> <span class="nc">RatingControl</span> <span class="p">:</span> <span class="n">TemplatedControl</span>
<span class="p">{</span>
<span class="p">}</span></code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_3_add_the_numberofstars_property">Step 3: Add the NumberOfStars-Property</h3>
<div class="paragraph">
<p>If we want to create a flexible control, we should not hardcode the number of stars. Instead, the developer should be able to define it inside a <code>Style</code>. Therefore we add a <code>StyledProperty</code> called <code>NumberOfStars</code>. The type of our property is <code>Integer</code>, the default value is <code>5</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cs"><span class="c1">/// &lt;summary&gt;</span>
<span class="c1">/// Defines the &lt;see cref="NumberOfStars"/&gt; property.</span>
<span class="c1">/// &lt;/summary&gt;</span>
<span class="c1">/// &lt;remarks&gt;</span>
<span class="c1">/// We define this property as a styled property, so you can set this property inside your style of the rating control.</span>
<span class="c1">/// &lt;/remarks&gt;</span>
<span class="k">public</span> <span class="k">static</span> <span class="k">readonly</span> <span class="n">StyledProperty</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;</span> <span class="n">NumberOfStarsProperty</span> <span class="p">=</span>
    <span class="n">AvaloniaProperty</span><span class="p">.</span><span class="n">Register</span><span class="p">&lt;</span><span class="n">RatingControl</span><span class="p">,</span> <span class="kt">int</span><span class="p">&gt;(</span>
        <span class="k">nameof</span><span class="p">(</span><span class="n">NumberOfStars</span><span class="p">),</span>          <span class="c1">// Sets the name of the property</span>
        <span class="n">defaultValue</span><span class="p">:</span> <span class="m">5</span><span class="p">,</span>                <span class="c1">// The default value of this property</span>
        <span class="n">coerce</span><span class="p">:</span> <span class="n">CoerceNumberOfStars</span><span class="p">);</span>   <span class="c1">// Ensures that we always have a valid number of stars</span>


<span class="c1">/// &lt;summary&gt;</span>
<span class="c1">/// Gets or sets the number of available stars</span>
<span class="c1">/// &lt;/summary&gt;</span>
<span class="k">public</span> <span class="kt">int</span> <span class="n">NumberOfStars</span>
<span class="p">{</span>
    <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="nf">GetValue</span><span class="p">(</span><span class="n">NumberOfStarsProperty</span><span class="p">);</span> <span class="p">}</span>
    <span class="k">set</span> <span class="p">{</span> <span class="nf">SetValue</span><span class="p">(</span><span class="n">NumberOfStarsProperty</span><span class="p">,</span> <span class="k">value</span><span class="p">);</span> <span class="p">}</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>The number of stars must not be smaller than one. A rating control without any star just makes no sense. We can ensure this by coercing the provided value. A coerce function needs to have the current control instance (<code>IAvaloniaObject instance</code>) and the value (<code>int value</code>) as a parameter and must return the coerced value:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cs"><span class="c1">/// &lt;summary&gt;</span>
<span class="c1">/// This function will coerce the &lt;see cref="NumberOfStars"/&gt; property. The minimum allowed number is 1</span>
<span class="c1">/// &lt;/summary&gt;</span>
<span class="c1">/// &lt;param name="sender"&gt;the RatingControl-instance calling this method&lt;/param&gt;</span>
<span class="c1">/// &lt;param name="value"&gt;the value to coerce&lt;/param&gt;</span>
<span class="c1">/// &lt;returns&gt;The coerced value&lt;/returns&gt;</span>
<span class="k">private</span> <span class="k">static</span> <span class="kt">int</span> <span class="nf">CoerceNumberOfStars</span><span class="p">(</span><span class="n">AvaloniaObject</span> <span class="n">instance</span><span class="p">,</span> <span class="kt">int</span> <span class="k">value</span><span class="p">)</span>
<span class="p">{</span>
    <span class="c1">// the value should not be lower than 1.</span>
    <span class="c1">// Hint: You can also return Math.Max(1, value)</span>
    <span class="k">if</span> <span class="p">(</span><span class="k">value</span> <span class="p">&lt;</span> <span class="m">1</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">return</span> <span class="m">1</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">else</span>
    <span class="p">{</span>
        <span class="k">return</span> <span class="k">value</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_4_add_the_value_property">Step 4: Add the Value-Property</h3>
<div class="paragraph">
<p>The next property we add is the <code>Value</code> property, which will hold the current rating. This property will be set by the user and is most likely set on each control instance. Moreover, as this property is meant to receive user input, we also want to add <a href="../../MVVM/ValidationSample">[validation support]</a>.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
We use a <code>DirectProperty</code> because it will improve performance and allows us to enable validation. The downside is, that this property cannot be set via <code>Styles</code>.
</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cs"><span class="c1">/// &lt;summary&gt;</span>
<span class="c1">/// Defines the &lt;see cref="Value"/&gt; property.</span>
<span class="c1">/// &lt;/summary&gt;</span>
<span class="c1">/// &lt;remarks&gt;</span>
<span class="c1">/// This property doesn't need to be styled. Therefore we can use a direct property, which improves performance and</span>
<span class="c1">/// allows us to add validation support.</span>
<span class="c1">/// &lt;/remarks&gt;</span>
<span class="k">public</span> <span class="k">static</span> <span class="k">readonly</span> <span class="n">DirectProperty</span><span class="p">&lt;</span><span class="n">RatingControl</span><span class="p">,</span> <span class="kt">int</span><span class="p">&gt;</span> <span class="n">ValueProperty</span> <span class="p">=</span>
    <span class="n">AvaloniaProperty</span><span class="p">.</span><span class="n">RegisterDirect</span><span class="p">&lt;</span><span class="n">RatingControl</span><span class="p">,</span> <span class="kt">int</span><span class="p">&gt;(</span>
        <span class="k">nameof</span><span class="p">(</span><span class="n">Value</span><span class="p">),</span>                            <span class="c1">// The name of the property</span>
        <span class="n">o</span> <span class="p">=&gt;</span> <span class="n">o</span><span class="p">.</span><span class="n">Value</span><span class="p">,</span>                             <span class="c1">// The getter of the property</span>
        <span class="p">(</span><span class="n">o</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">o</span><span class="p">.</span><span class="n">Value</span> <span class="p">=</span> <span class="n">v</span><span class="p">,</span>                    <span class="c1">// The setter of the property</span>
        <span class="n">defaultBindingMode</span><span class="p">:</span> <span class="n">BindingMode</span><span class="p">.</span><span class="n">TwoWay</span><span class="p">,</span>   <span class="c1">// We change the default binding mode to be two-way, so if the user selects a new value, it will automatically update the bound property</span>
        <span class="n">enableDataValidation</span><span class="p">:</span> <span class="k">true</span><span class="p">);</span>              <span class="c1">// Enables DataValidation</span>

<span class="c1">// For direct properties we need to have a backing field</span>
<span class="k">private</span> <span class="kt">int</span> <span class="n">_value</span><span class="p">;</span>

<span class="c1">/// &lt;summary&gt;</span>
<span class="c1">/// Gets or sets the current value</span>
<span class="c1">/// &lt;/summary&gt;</span>
<span class="k">public</span> <span class="kt">int</span> <span class="n">Value</span>
<span class="p">{</span>
    <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">_value</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">set</span> <span class="p">{</span> <span class="nf">SetAndRaise</span><span class="p">(</span><span class="n">ValueProperty</span><span class="p">,</span> <span class="k">ref</span> <span class="n">_value</span><span class="p">,</span> <span class="k">value</span><span class="p">);</span> <span class="p">}</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
In this sample the value is of type <code>int</code>, so only full stars can be shown. If you want to add support for half stars, consider to use <code>float</code> or <code>double</code>.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>We set <code>enableDataValidation</code> to <code>true</code>. But this is not enough for validation support. We also need to override <code>UpdateDataValidation</code>. This function will be called whenever a property asks for validation. Most likely we want to use set an error on the <code>DataValidationErrors</code>-control:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cs"><span class="c1">/// &lt;summary&gt;</span>
<span class="c1">/// Called to update the validation state for properties for which data validation is</span>
<span class="c1">/// enabled.</span>
<span class="c1">/// &lt;/summary&gt;</span>
<span class="c1">/// &lt;param name="property"&gt;The property.&lt;/param&gt;</span>
<span class="c1">/// &lt;param name="state"&gt;The current data binding state.&lt;/param&gt;</span>
<span class="c1">/// &lt;param name="error"&gt;The Exception that was passed&lt;/param&gt;</span>
<span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">UpdateDataValidation</span><span class="p">(</span><span class="n">AvaloniaProperty</span> <span class="n">property</span><span class="p">,</span> <span class="n">BindingValueType</span> <span class="n">state</span><span class="p">,</span> <span class="n">Exception</span><span class="p">?</span> <span class="n">error</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">base</span><span class="p">.</span><span class="nf">UpdateDataValidation</span><span class="p">(</span><span class="n">property</span><span class="p">,</span> <span class="n">state</span><span class="p">,</span> <span class="n">error</span><span class="p">);</span>

    <span class="k">if</span><span class="p">(</span><span class="n">property</span> <span class="p">==</span> <span class="n">ValueProperty</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">DataValidationErrors</span><span class="p">.</span><span class="nf">SetError</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="n">error</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_5_add_the_stars_property">Step 5: Add the Stars-Property</h3>
<div class="paragraph">
<p>Now that we have the number of stars and the value property, we need a way to dynamically represent the stars. While we technically can add the stars in code, we will use a different approach here. The idea is, that we add a read-only helper property called <code>Stars</code>. This property will just provide a <code>Range</code> of <code>Integers</code>. In our <code>Style</code> we can use this property to draw the stars.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cs"><span class="c1">/// &lt;summary&gt;</span>
<span class="c1">/// Defines the &lt;see cref="Stars"/&gt; property.</span>
<span class="c1">/// &lt;/summary&gt;</span>
<span class="c1">/// &lt;remarks&gt;</span>
<span class="c1">/// ´This property holds a read-only array of stars.</span>
<span class="c1">/// &lt;/remarks&gt;</span>
<span class="k">public</span> <span class="k">static</span> <span class="k">readonly</span> <span class="n">DirectProperty</span><span class="p">&lt;</span><span class="n">RatingControl</span><span class="p">,</span> <span class="n">IEnumerable</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;&gt;</span> <span class="n">StarsProperty</span> <span class="p">=</span>
    <span class="n">AvaloniaProperty</span><span class="p">.</span><span class="n">RegisterDirect</span> <span class="p">&lt;</span> <span class="n">RatingControl</span><span class="p">,</span> <span class="n">IEnumerable</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;&gt;(</span>
        <span class="k">nameof</span><span class="p">(</span><span class="n">Stars</span><span class="p">),</span>              <span class="c1">// The name of the Property</span>
        <span class="n">o</span> <span class="p">=&gt;</span> <span class="n">o</span><span class="p">.</span><span class="n">Stars</span><span class="p">);</span>   <span class="c1">// The getter. As we don't add a setter, this property is read-only</span>

<span class="c1">// For read-only properties we need to have a backing field. The default value is [1..5]</span>
<span class="k">private</span> <span class="n">IEnumerable</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;</span> <span class="n">_stars</span> <span class="p">=</span> <span class="n">Enumerable</span><span class="p">.</span><span class="nf">Range</span><span class="p">(</span><span class="m">1</span><span class="p">,</span> <span class="m">5</span><span class="p">);</span>

<span class="c1">/// &lt;summary&gt;</span>
<span class="c1">/// Gets the current collection of visible stars</span>
<span class="c1">/// &lt;/summary&gt;</span>
<span class="k">public</span> <span class="n">IEnumerable</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;</span> <span class="n">Stars</span>
<span class="p">{</span>
    <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">_stars</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">private</span> <span class="k">set</span> <span class="p">{</span> <span class="nf">SetAndRaise</span><span class="p">(</span><span class="n">StarsProperty</span><span class="p">,</span> <span class="k">ref</span> <span class="n">_stars</span><span class="p">,</span> <span class="k">value</span><span class="p">);</span> <span class="p">}</span> <span class="c1">// make sure the setter is private</span>
<span class="p">}</span></code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_6_update_the_stars_property">Step 6: Update the Stars-Property</h3>
<div class="paragraph">
<p>We need a way to update the <code>Stars</code> property whenever the <code>NumberOfStars</code>-Property has changed. So let&#8217;s add a method to do this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cs"><span class="c1">// called when the number of stars changed</span>
<span class="k">private</span> <span class="k">void</span> <span class="nf">UpdateStars</span><span class="p">()</span>
<span class="p">{</span>
    <span class="c1">// Stars is an array from 1 to NumberOfStars</span>
    <span class="n">Stars</span> <span class="p">=</span> <span class="n">Enumerable</span><span class="p">.</span><span class="nf">Range</span><span class="p">(</span><span class="m">1</span><span class="p">,</span> <span class="n">NumberOfStars</span><span class="p">);</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>In Avalonia each control has a <code>PropertyChanged</code>-event, which will be raised every time a property changed. We can override <code>OnPropertyChanged</code> in our control to handle this event:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cs"><span class="c1">// We override OnPropertyChanged of the base class. That way we can react on property changes</span>
<span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">OnPropertyChanged</span><span class="p">(</span><span class="n">AvaloniaPropertyChangedEventArgs</span> <span class="n">change</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">base</span><span class="p">.</span><span class="nf">OnPropertyChanged</span><span class="p">(</span><span class="n">change</span><span class="p">);</span>

    <span class="c1">// if the changed property is the NumberOfStarsProperty, we need to update the stars</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">change</span><span class="p">.</span><span class="n">Property</span> <span class="p">==</span> <span class="n">NumberOfStarsProperty</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="nf">UpdateStars</span><span class="p">();</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Moreover we want to update the <code>Stars</code>-Property as soon as a new instance of our control was created. We can do this inside the constructor:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cs"><span class="k">public</span> <span class="nf">RatingControl</span><span class="p">()</span>
<span class="p">{</span>
    <span class="c1">// When a new instance of the control is created, we need to update the rating stars visible</span>
    <span class="nf">UpdateStars</span><span class="p">();</span>
<span class="p">}</span></code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_7_add_user_interaction">Step 7: Add user interaction</h3>
<div class="paragraph">
<p>Okay, all properties we need are there. But wait, how should the user interact with our control? At the moment, we do not handle any user interaction. At least when a user clicks on a star, the value should be set to the number that this star has. To achieve this we require the <code>ControlTemplate</code> to provide an <code>ItemsControl</code> called <code>PART_StarsPresenter</code>. Use the <code>TemplatePart-Attribute</code> to indicate this.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cs"><span class="c1">// This Attribute specifies that "PART_StarsPresenter" is a control, which must be present in the Control-Template</span>
<span class="p">[</span><span class="nf">TemplatePart</span><span class="p">(</span><span class="s">"PART_StarsPresenter"</span><span class="p">,</span> <span class="k">typeof</span><span class="p">(</span><span class="n">ItemsControl</span><span class="p">))]</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">RatingControl</span> <span class="p">:</span> <span class="n">TemplatedControl</span>
<span class="p">{</span>
    <span class="p">...</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>In order to hold a reference to the named <code>ItemsControl</code>, we add a private field inside our <code>Control</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cs"><span class="c1">// this field holds a reference to the part in the control template that holds the rating stars</span>
<span class="k">private</span> <span class="n">ItemsControl</span><span class="p">?</span> <span class="n">_starsPresenter</span><span class="p">;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Last but not least we need a way to find this control inside our <code>ControlTemplate</code>. Whenever a new <code>ControlTemplate</code> is applied, the method <code>OnApplyTemplate</code> will be called. We can override it like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cs"><span class="c1">// We override what happens when the control template is being applied.</span>
<span class="c1">// That way we can for example listen to events of controls which are part of the template</span>
<span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">OnApplyTemplate</span><span class="p">(</span><span class="n">TemplateAppliedEventArgs</span> <span class="n">e</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">base</span><span class="p">.</span><span class="nf">OnApplyTemplate</span><span class="p">(</span><span class="n">e</span><span class="p">);</span>

    <span class="c1">// if we had a control template before, we need to unsubscribe any event listeners</span>
    <span class="k">if</span><span class="p">(</span><span class="n">_starsPresenter</span> <span class="k">is</span> <span class="n">not</span> <span class="k">null</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">_starsPresenter</span><span class="p">.</span><span class="n">PointerReleased</span><span class="p">-=</span> <span class="n">StarsPresenter_PointerReleased</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// try to find the control with the given name</span>
    <span class="n">_starsPresenter</span> <span class="p">=</span> <span class="n">e</span><span class="p">.</span><span class="n">NameScope</span><span class="p">.</span><span class="nf">Find</span><span class="p">(</span><span class="s">"PART_StarsPresenter"</span><span class="p">)</span> <span class="k">as</span> <span class="n">ItemsControl</span><span class="p">;</span>

    <span class="c1">// listen to pointer-released events on the stars presenter.</span>
    <span class="k">if</span><span class="p">(</span><span class="n">_starsPresenter</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">_starsPresenter</span><span class="p">.</span><span class="n">PointerReleased</span> <span class="p">+=</span> <span class="n">StarsPresenter_PointerReleased</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>As you can see we did the following:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>run the base method to make sure everything is set up correctly</p>
</li>
<li>
<p>unsubscribe from any previous event listeners</p>
</li>
<li>
<p>find the named control in the new template to apply</p>
</li>
<li>
<p>listen to the <code>PointerReleased</code>-event of the found <code>ItemsControl</code></p>
</li>
</ol>
</div>
<div class="paragraph">
<p>By convention we know that the <code>Items</code> of our <code>ItemsControl</code> will be a <code>Path</code>. We make use of this convention by checking if the <code>Source</code> of the event is a <code>Path</code> and if it was, we know its <code>DataContext</code> will be an <code>Integer</code>. Therefore the new <code>Value</code> of our <code>RatingControl</code> is set to the given <code>Integer</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cs"><span class="k">private</span> <span class="k">void</span> <span class="nf">StarsPresenter_PointerReleased</span><span class="p">(</span><span class="kt">object</span><span class="p">?</span> <span class="n">sender</span><span class="p">,</span> <span class="n">Avalonia</span><span class="p">.</span><span class="n">Input</span><span class="p">.</span><span class="n">PointerReleasedEventArgs</span> <span class="n">e</span><span class="p">)</span>
<span class="p">{</span>
    <span class="c1">// e.Source is the original source of this event. In our case, if the user clicked on a star, the original source is a Path.</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">Source</span> <span class="k">is</span> <span class="n">Path</span> <span class="n">star</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="c1">// The DataContext of the star is one of the numbers we have in the Stars-Collection.</span>
        <span class="c1">// Let's cast the DataContext to an int. If that cast fails, use "0" as a fallback.</span>
        <span class="n">Value</span> <span class="p">=</span> <span class="n">star</span><span class="p">.</span><span class="n">DataContext</span> <span class="k">as</span> <span class="kt">int</span><span class="p">?</span> <span class="p">??</span> <span class="m">0</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Because we use the <code>as</code>-operator, our <code>Value</code> would become <code>null</code> if the <code>DataContext</code> could not be converted to <code>int</code> for any reason and thus crash the App. To prevent such a crash we use <code>0</code> as a fallback.
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_step_8_add_a_controltheme_for_the_ratingcontrol">Step 8: Add a <code>ControlTheme</code> for the RatingControl</h3>
<div class="paragraph">
<p>While we can already add a <code>RatingControl</code> to our View, we will see nothing as there is no <code>RatingControl</code> available. To change this we add another folder called <code>Themes</code>. Add a file called <code>RatingStyles.axaml</code> which is of type <code>ResourceDictionary (Avalonia)</code>.</p>
</div>
<div class="paragraph">
<p>First of all we need to add the needed namespaces to our <code>ResourceDictionary</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="xml"><span class="nt">&lt;ResourceDictionary</span> <span class="na">xmlns=</span><span class="s">"https://github.com/avaloniaui"</span>
		<span class="na">xmlns:controls=</span><span class="s">"using:RatingControlSample.Controls"</span>
		<span class="na">xmlns:converter=</span><span class="s">"using:RatingControlSample.Converter"</span>
        <span class="na">xmlns:x=</span><span class="s">"http://schemas.microsoft.com/winfx/2006/xaml"</span><span class="nt">&gt;</span>
<span class="nt">&lt;/ResourceDictionary&gt;</span></code></pre>
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>If you want to have preview of the <code>ResourceDictionary</code>, just add one or more <code>RatingControls</code> to the <code>Design.PreviewWith</code>-section:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="xml"><span class="nt">&lt;Design.PreviewWith&gt;</span>
    <span class="nt">&lt;StackPanel</span> <span class="na">Spacing=</span><span class="s">"10"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;controls:RatingControl</span> <span class="na">Value=</span><span class="s">"0"</span> <span class="na">NumberOfStars=</span><span class="s">"5"</span> <span class="nt">/&gt;</span>
        <span class="nt">&lt;controls:RatingControl</span> <span class="na">Value=</span><span class="s">"2"</span> <span class="na">NumberOfStars=</span><span class="s">"5"</span> <span class="nt">/&gt;</span>
        <span class="nt">&lt;controls:RatingControl</span> <span class="na">Value=</span><span class="s">"6"</span> <span class="na">NumberOfStars=</span><span class="s">"6"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;/StackPanel&gt;</span>
<span class="nt">&lt;/Design.PreviewWith&gt;</span></code></pre>
</div>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Now we can add the needed <code>ControlTheme</code> to represent our <code>RatingControl</code>. The important part is the <code>ControlTemplate</code> which has the following hierarchy:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="xml">  <span class="c">&lt;!--This is the ControlTheme for our RatingControl--&gt;</span>
  <span class="nt">&lt;ControlTheme</span> <span class="na">x:Key=</span><span class="s">"{x:Type controls:RatingControl}"</span> <span class="na">TargetType=</span><span class="s">"controls:RatingControl"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;ControlTheme.Resources&gt;</span>
      <span class="c">&lt;!--We need to add our IsSmallerOrEqualConverter here as a Resource--&gt;</span>
      <span class="nt">&lt;converter:IsSmallerOrEqualConverter</span> <span class="na">x:Key=</span><span class="s">"IsSmallerOrEqualConverter"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;/ControlTheme.Resources&gt;</span>
    <span class="c">&lt;!--Every TemplatedControl needs to have a ControlTemplate applied. In this setter we define it.--&gt;</span>
    <span class="nt">&lt;Setter</span> <span class="na">Property=</span><span class="s">"Template"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;ControlTemplate&gt;</span>

        <span class="c">&lt;!--We wrap our content inside a DataValidationErrors-control, so error messages are displayed properly--&gt;</span>
        <span class="nt">&lt;DataValidationErrors&gt;</span>
          <span class="c">&lt;!--This is our stars-presenter. Make sure to set the name, so the control can be found.--&gt;</span>
          <span class="nt">&lt;ItemsControl</span> <span class="na">x:Name=</span><span class="s">"PART_StarsPresenter"</span>
                  <span class="na">ItemsSource=</span><span class="s">"{TemplateBinding Stars}"</span><span class="nt">&gt;</span>
            <span class="c">&lt;!--We want to have the stars drawn horizontally. Therefore we change the ItemsPanel accordingly--&gt;</span>
            <span class="nt">&lt;ItemsControl.ItemsPanel&gt;</span>
              <span class="nt">&lt;ItemsPanelTemplate&gt;</span>
                <span class="nt">&lt;StackPanel</span> <span class="na">Orientation=</span><span class="s">"Horizontal"</span>
                      <span class="na">Spacing=</span><span class="s">"5"</span> <span class="nt">/&gt;</span>
              <span class="nt">&lt;/ItemsPanelTemplate&gt;</span>
            <span class="nt">&lt;/ItemsControl.ItemsPanel&gt;</span>

            <span class="c">&lt;!--Items is an array if integer. Let's add a Path as the DataTemplate--&gt;</span>
            <span class="nt">&lt;ItemsControl.ItemTemplate&gt;</span>
              <span class="nt">&lt;DataTemplate&gt;</span>
                <span class="nt">&lt;Path</span> <span class="na">Classes=</span><span class="s">"star"</span><span class="nt">&gt;</span>
                  <span class="c">&lt;!--We enable or disable classes through a boolean value. We use our IsSmallerOrEqualConverter to do so. --&gt;</span>
                  <span class="nt">&lt;Classes.selected&gt;</span>
                    <span class="nt">&lt;MultiBinding</span> <span class="na">Converter=</span><span class="s">"{StaticResource IsSmallerOrEqualConverter}"</span><span class="nt">&gt;</span>
                      <span class="c">&lt;!--This is our dataContext, so the number of this item--&gt;</span>
                      <span class="nt">&lt;Binding</span> <span class="nt">/&gt;</span>
                      <span class="c">&lt;!--This is the binding to the RatingControls current value--&gt;</span>
                      <span class="nt">&lt;Binding</span> <span class="na">RelativeSource=</span><span class="s">"{RelativeSource AncestorType=controls:RatingControl}"</span> <span class="na">Path=</span><span class="s">"Value"</span> <span class="nt">/&gt;</span>
                    <span class="nt">&lt;/MultiBinding&gt;</span>
                  <span class="nt">&lt;/Classes.selected&gt;</span>
                <span class="nt">&lt;/Path&gt;</span>
              <span class="nt">&lt;/DataTemplate&gt;</span>
            <span class="nt">&lt;/ItemsControl.ItemTemplate&gt;</span>

          <span class="nt">&lt;/ItemsControl&gt;</span>
        <span class="nt">&lt;/DataValidationErrors&gt;</span>

      <span class="nt">&lt;/ControlTemplate&gt;</span>
    <span class="nt">&lt;/Setter&gt;</span>
  <span class="nt">&lt;/ControlTheme&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>In the above snippet you can see that the <code>ControlTemplate</code> our <code>RatingControl</code> has the following structure:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>ControlTemplate                 -&gt; This is our root node
   &gt; DataValidationErrors       -&gt; This control will take care of displaying any validation errors
      &gt; ItemsControl            -&gt; Used to display the Stars.
         o ItemsPanelTemplate   -&gt; We change the ItemsPanelTemplate in order to display the Stars horizontally
         o ItemTemplate         -&gt; We add an ItemTemplate to render the star as a Path</pre>
</div>
</div>
<div class="paragraph">
<p>Let us inspect the <code>ItemTemplate</code> a bit further. It is a <code>Path</code> with the class <code>star</code> applied. You can see the <code>Style</code> for the class below. It sets the <code>Data</code> and other <code>Properties</code> to render a single <code>Star</code> in the unselected state.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="xml"><span class="c">&lt;!--This Style is for a Path which has the Class "star" applied.--&gt;</span>
<span class="nt">&lt;Style</span> <span class="na">Selector=</span><span class="s">"Path.star"</span> <span class="nt">&gt;</span>
    <span class="nt">&lt;Setter</span> <span class="na">Property=</span><span class="s">"Data"</span> <span class="na">Value=</span><span class="s">"M 3.9687501,0 5.1351364,2.3633569 7.7432556,2.7423389 5.8560028,4.5819556 6.3015226,7.1795363 3.96875,5.953125 1.6359772,7.1795361 2.0814972,4.5819556 0.19424448,2.7423387 2.8023636,2.3633569 Z"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;Setter</span> <span class="na">Property=</span><span class="s">"Width"</span> <span class="na">Value=</span><span class="s">"32"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;Setter</span> <span class="na">Property=</span><span class="s">"Height"</span> <span class="na">Value=</span><span class="s">"32"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;Setter</span> <span class="na">Property=</span><span class="s">"Margin"</span> <span class="na">Value=</span><span class="s">"5"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;Setter</span> <span class="na">Property=</span><span class="s">"Fill"</span> <span class="na">Value=</span><span class="s">"White"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;Setter</span> <span class="na">Property=</span><span class="s">"Stroke"</span> <span class="na">Value=</span><span class="s">"Gray"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;Setter</span> <span class="na">Property=</span><span class="s">"StrokeThickness"</span> <span class="na">Value=</span><span class="s">"2"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;Setter</span> <span class="na">Property=</span><span class="s">"Stretch"</span> <span class="na">Value=</span><span class="s">"Uniform"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;/Style&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>We add another class <code>selected</code>. We make use of the fact that we can <a href="https://docs.avaloniaui.net/docs/styling/styles#style-classes">[add or remove <code>Style-classes</code>]</a> in Avalonia. We use a <a href="https://github.com/AvaloniaUI/Avalonia.Samples/tree/main/src/Avalonia.Samples/MVVM/ValueConversionSample">[<code>MultiConverter</code>]</a> called <code>IsSmallerOrEqualConverter</code> which compare the stars number with the selected value and return <code>true</code> if the number is smaller or equal to the selected value. The code of the converter is shown below.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="xml"><span class="nt">&lt;Style</span> <span class="na">Selector=</span><span class="s">"Path.selected"</span> <span class="nt">&gt;</span>
    <span class="nt">&lt;Setter</span> <span class="na">Property=</span><span class="s">"Fill"</span> <span class="na">Value=</span><span class="s">"Gold"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;Setter</span> <span class="na">Property=</span><span class="s">"Stroke"</span> <span class="na">Value=</span><span class="s">"Goldenrod"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;/Style&gt;</span></code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="cs"><span class="c1">/// &lt;summary&gt;</span>
<span class="c1">/// A converter that compares two integers and returns true if the first number is smaller or equal to the second number</span>
<span class="c1">/// &lt;/summary&gt;</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">IsSmallerOrEqualConverter</span> <span class="p">:</span> <span class="n">IMultiValueConverter</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="kt">object</span><span class="p">?</span> <span class="nf">Convert</span><span class="p">(</span><span class="n">IList</span><span class="p">&lt;</span><span class="kt">object</span><span class="p">?&gt;</span> <span class="n">values</span><span class="p">,</span> <span class="n">Type</span> <span class="n">targetType</span><span class="p">,</span> <span class="kt">object</span><span class="p">?</span> <span class="n">parameter</span><span class="p">,</span> <span class="n">CultureInfo</span> <span class="n">culture</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">values</span><span class="p">.</span><span class="n">Count</span> <span class="p">!=</span> <span class="m">2</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="nf">ArgumentException</span><span class="p">(</span><span class="s">"Expected exactly two numbers"</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="kt">var</span> <span class="n">firstNumber</span> <span class="p">=</span> <span class="n">values</span><span class="p">[</span><span class="m">0</span><span class="p">]</span> <span class="k">as</span> <span class="kt">int</span><span class="p">?;</span>
        <span class="kt">var</span> <span class="n">secondNumber</span> <span class="p">=</span> <span class="n">values</span><span class="p">[</span><span class="m">1</span><span class="p">]</span> <span class="k">as</span> <span class="kt">int</span><span class="p">?;</span>

        <span class="k">return</span> <span class="n">firstNumber</span> <span class="p">&lt;=</span> <span class="n">secondNumber</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Last but not least we want a visual feedback if the user hovers a star with their mouse device. So we add a <code>Style</code> with the class name <code>star</code> and the <a href="https://docs.avaloniaui.net/docs/styling/styles#pseudoclasses">[pseudoclass]</a> <code>:pointerover</code>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="xml"><span class="nt">&lt;Style</span> <span class="na">Selector=</span><span class="s">"Path.star:pointerover"</span> <span class="nt">&gt;</span>
    <span class="nt">&lt;Setter</span> <span class="na">Property=</span><span class="s">"RenderTransform"</span> <span class="na">Value=</span><span class="s">"scale(1.3)"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;Setter</span> <span class="na">Property=</span><span class="s">"Fill"</span> <span class="na">Value=</span><span class="s">"Goldenrod"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;/Style&gt;</span></code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
In Avalonia 11.x <code>Styles</code>(see <a href="https://github.com/AvaloniaUI/Avalonia.Samples/blob/be2895d5461fd4748044d5a81c571547965b9f9d/src/Avalonia.Samples/CustomControls/RatingControlSample/README.adoc#step-8-add-a-style-for-the-ratingcontrol">this</a>) is supported, but since 11.0 <code>ControlThemes</code> are preferred.
</td>
</tr>
</table>
</div>
<div class="sidebarblock">
<div class="content">
<div class="title">Reference</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://docs.avaloniaui.net/docs/basics/user-interface/styling/styles">Styles</a></p>
</li>
<li>
<p><a href="https://docs.avaloniaui.net/docs/basics/user-interface/styling/control-themes">Control Themes</a></p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_9_theme_variant">Step 9: Theme Variant</h3>
<div class="paragraph">
<p>if you want to add support to the theme variant, you need to replace the encoded setter value with <code>DynamicResource</code>. In this case we would like a different filling and stroke of the Path based on the theme variant.</p>
</div>
<div class="paragraph">
<p>To do this, modify our style selector like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="xml">    <span class="c">&lt;!--This Style is for a Path which has the Class "star" applied.--&gt;</span>
    <span class="nt">&lt;Style</span> <span class="na">Selector=</span><span class="s">"Path.star"</span> <span class="nt">&gt;</span>
        <span class="nt">&lt;Setter</span> <span class="na">Property=</span><span class="s">"Data"</span> <span class="na">Value=</span><span class="s">"M 3.9687501,0 5.1351364,2.3633569 7.7432556,2.7423389 5.8560028,4.5819556 6.3015226,7.1795363 3.96875,5.953125 1.6359772,7.1795361 2.0814972,4.5819556 0.19424448,2.7423387 2.8023636,2.3633569 Z"</span> <span class="nt">/&gt;</span>
        <span class="nt">&lt;Setter</span> <span class="na">Property=</span><span class="s">"Width"</span> <span class="na">Value=</span><span class="s">"32"</span> <span class="nt">/&gt;</span>
        <span class="nt">&lt;Setter</span> <span class="na">Property=</span><span class="s">"Height"</span> <span class="na">Value=</span><span class="s">"32"</span> <span class="nt">/&gt;</span>
        <span class="nt">&lt;Setter</span> <span class="na">Property=</span><span class="s">"Margin"</span> <span class="na">Value=</span><span class="s">"5"</span> <span class="nt">/&gt;</span>
        <span class="nt">&lt;Setter</span> <span class="na">Property=</span><span class="s">"Fill"</span> <span class="na">Value=</span><span class="s">"{DynamicResource RatingControlUnselectedBrush}"</span> <span class="nt">/&gt;</span>
        <span class="nt">&lt;Setter</span> <span class="na">Property=</span><span class="s">"Stroke"</span> <span class="na">Value=</span><span class="s">"{DynamicResource RatingControlUnselectedStrokenBrush}"</span> <span class="nt">/&gt;</span>
        <span class="nt">&lt;Setter</span> <span class="na">Property=</span><span class="s">"StrokeThickness"</span> <span class="na">Value=</span><span class="s">"2"</span> <span class="nt">/&gt;</span>
        <span class="nt">&lt;Setter</span> <span class="na">Property=</span><span class="s">"Stretch"</span> <span class="na">Value=</span><span class="s">"Uniform"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;/Style&gt;</span>

    <span class="nt">&lt;Style</span> <span class="na">Selector=</span><span class="s">"Path.selected"</span> <span class="nt">&gt;</span>
        <span class="nt">&lt;Setter</span> <span class="na">Property=</span><span class="s">"Fill"</span> <span class="na">Value=</span><span class="s">"{DynamicResource RatingControlSelectedBrush}"</span> <span class="nt">/&gt;</span>
        <span class="nt">&lt;Setter</span> <span class="na">Property=</span><span class="s">"Stroke"</span> <span class="na">Value=</span><span class="s">"{DynamicResource RatingControlSelectedStrokenBrush}"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;/Style&gt;</span>

    <span class="nt">&lt;Style</span> <span class="na">Selector=</span><span class="s">"Path.star:pointerover"</span> <span class="nt">&gt;</span>
        <span class="nt">&lt;Setter</span> <span class="na">Property=</span><span class="s">"RenderTransform"</span> <span class="na">Value=</span><span class="s">"scale(1.3)"</span> <span class="nt">/&gt;</span>
        <span class="nt">&lt;Setter</span> <span class="na">Property=</span><span class="s">"Fill"</span> <span class="na">Value=</span><span class="s">"{DynamicResource RatingControlSelectedStrokenBrush}"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;/Style&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>now, will be define Resource for each Theme Variant</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="xml">  <span class="c">&lt;!-- Define the Theme Variants --&gt;</span>
  <span class="nt">&lt;ResourceDictionary.ThemeDictionaries&gt;</span>
    <span class="nt">&lt;ResourceDictionary</span> <span class="na">x:Key=</span><span class="s">"Default"</span><span class="nt">&gt;</span>
      <span class="c">&lt;!-- Selected Brushes--&gt;</span>
      <span class="nt">&lt;SolidColorBrush</span> <span class="na">x:Key=</span><span class="s">"RatingControlSelectedBrush"</span> <span class="na">Color=</span><span class="s">"Gold"</span><span class="nt">/&gt;</span>
      <span class="nt">&lt;SolidColorBrush</span> <span class="na">x:Key=</span><span class="s">"RatingControlSelectedStrokenBrush"</span> <span class="na">Color=</span><span class="s">"Goldenrod"</span><span class="nt">/&gt;</span>
      <span class="c">&lt;!-- Unselected Brushes--&gt;</span>
      <span class="nt">&lt;SolidColorBrush</span> <span class="na">x:Key=</span><span class="s">"RatingControlUnselectedBrush"</span> <span class="na">Color=</span><span class="s">"White"</span><span class="nt">/&gt;</span>
      <span class="nt">&lt;SolidColorBrush</span> <span class="na">x:Key=</span><span class="s">"RatingControlUnselectedStrokenBrush"</span> <span class="na">Color=</span><span class="s">"Gray"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;/ResourceDictionary&gt;</span>
    <span class="nt">&lt;ResourceDictionary</span> <span class="na">x:Key=</span><span class="s">"Light"</span><span class="nt">&gt;</span>
      <span class="c">&lt;!-- Selected Brushes--&gt;</span>
      <span class="nt">&lt;SolidColorBrush</span> <span class="na">x:Key=</span><span class="s">"RatingControlSelectedBrush"</span> <span class="na">Color=</span><span class="s">"Gold"</span><span class="nt">/&gt;</span>
      <span class="nt">&lt;SolidColorBrush</span> <span class="na">x:Key=</span><span class="s">"RatingControlSelectedStrokenBrush"</span> <span class="na">Color=</span><span class="s">"Gray"</span><span class="nt">/&gt;</span>
      <span class="c">&lt;!-- Unselected Brushes--&gt;</span>
      <span class="nt">&lt;SolidColorBrush</span> <span class="na">x:Key=</span><span class="s">"RatingControlUnselectedBrush"</span> <span class="na">Color=</span><span class="s">"White"</span><span class="nt">/&gt;</span>
      <span class="nt">&lt;SolidColorBrush</span> <span class="na">x:Key=</span><span class="s">"RatingControlUnselectedStrokenBrush"</span> <span class="na">Color=</span><span class="s">"Gray"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;/ResourceDictionary&gt;</span>
    <span class="nt">&lt;ResourceDictionary</span> <span class="na">x:Key=</span><span class="s">"Dark"</span><span class="nt">&gt;</span>
      <span class="c">&lt;!-- Selected Brushes--&gt;</span>
      <span class="nt">&lt;SolidColorBrush</span> <span class="na">x:Key=</span><span class="s">"RatingControlSelectedBrush"</span> <span class="na">Color=</span><span class="s">"Red"</span><span class="nt">/&gt;</span>
      <span class="nt">&lt;SolidColorBrush</span> <span class="na">x:Key=</span><span class="s">"RatingControlSelectedStrokenBrush"</span> <span class="na">Color=</span><span class="s">"White"</span><span class="nt">/&gt;</span>
      <span class="c">&lt;!-- Unselected Brushes--&gt;</span>
      <span class="nt">&lt;SolidColorBrush</span> <span class="na">x:Key=</span><span class="s">"RatingControlUnselectedBrush"</span> <span class="na">Color=</span><span class="s">"Transparent"</span><span class="nt">/&gt;</span>
      <span class="nt">&lt;SolidColorBrush</span> <span class="na">x:Key=</span><span class="s">"RatingControlUnselectedStrokenBrush"</span> <span class="na">Color=</span><span class="s">"White"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;/ResourceDictionary&gt;</span>
  <span class="nt">&lt;/ResourceDictionary.ThemeDictionaries&gt;</span></code></pre>
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>If you want to have preview of the <code>ResourceDictionary</code> support Theme Variants, just add one or more <code>RatingControls</code> to the <code>Design.PreviewWith</code>-section:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="xml">  <span class="c">&lt;!-- Design time preview --&gt;</span>
  <span class="nt">&lt;Design.PreviewWith&gt;</span>
    <span class="nt">&lt;StackPanel</span> <span class="na">Width=</span><span class="s">"400"</span> <span class="na">Spacing=</span><span class="s">"10"</span><span class="nt">&gt;</span>
      <span class="c">&lt;!-- Force using default Theme Variant --&gt;</span>
      <span class="nt">&lt;ThemeVariantScope</span> <span class="na">RequestedThemeVariant=</span><span class="s">"Default"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;StackPanel</span> <span class="na">Spacing=</span><span class="s">"10"</span> <span class="na">Background=</span><span class="s">"{DynamicResource SystemRegionBrush}"</span><span class="nt">&gt;</span>
          <span class="nt">&lt;controls:RatingControl</span> <span class="na">Value=</span><span class="s">"0"</span> <span class="na">NumberOfStars=</span><span class="s">"5"</span> <span class="nt">/&gt;</span>
          <span class="nt">&lt;controls:RatingControl</span> <span class="na">Value=</span><span class="s">"2"</span> <span class="na">NumberOfStars=</span><span class="s">"5"</span> <span class="nt">/&gt;</span>
          <span class="nt">&lt;controls:RatingControl</span> <span class="na">Value=</span><span class="s">"6"</span> <span class="na">NumberOfStars=</span><span class="s">"6"</span> <span class="nt">/&gt;</span>
        <span class="nt">&lt;/StackPanel&gt;</span>
      <span class="nt">&lt;/ThemeVariantScope&gt;</span>
      <span class="c">&lt;!-- Force using Light Theme Variant --&gt;</span>
      <span class="nt">&lt;ThemeVariantScope</span> <span class="na">RequestedThemeVariant=</span><span class="s">"Light"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;StackPanel</span> <span class="na">Spacing=</span><span class="s">"10"</span> <span class="na">Background=</span><span class="s">"{DynamicResource SystemRegionBrush}"</span><span class="nt">&gt;</span>
          <span class="nt">&lt;controls:RatingControl</span> <span class="na">Value=</span><span class="s">"0"</span> <span class="na">NumberOfStars=</span><span class="s">"5"</span> <span class="nt">/&gt;</span>
          <span class="nt">&lt;controls:RatingControl</span> <span class="na">Value=</span><span class="s">"2"</span> <span class="na">NumberOfStars=</span><span class="s">"5"</span> <span class="nt">/&gt;</span>
          <span class="nt">&lt;controls:RatingControl</span> <span class="na">Value=</span><span class="s">"6"</span> <span class="na">NumberOfStars=</span><span class="s">"6"</span> <span class="nt">/&gt;</span>
        <span class="nt">&lt;/StackPanel&gt;</span>
      <span class="nt">&lt;/ThemeVariantScope&gt;</span>
      <span class="c">&lt;!-- Force using Dark Theme Variant --&gt;</span>
      <span class="nt">&lt;ThemeVariantScope</span> <span class="na">RequestedThemeVariant=</span><span class="s">"Dark"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;StackPanel</span> <span class="na">Spacing=</span><span class="s">"10"</span> <span class="na">Background=</span><span class="s">"{DynamicResource SystemRegionBrush}"</span><span class="nt">&gt;</span>
          <span class="nt">&lt;controls:RatingControl</span> <span class="na">Value=</span><span class="s">"0"</span> <span class="na">NumberOfStars=</span><span class="s">"5"</span> <span class="nt">/&gt;</span>
          <span class="nt">&lt;controls:RatingControl</span> <span class="na">Value=</span><span class="s">"2"</span> <span class="na">NumberOfStars=</span><span class="s">"5"</span> <span class="nt">/&gt;</span>
          <span class="nt">&lt;controls:RatingControl</span> <span class="na">Value=</span><span class="s">"6"</span> <span class="na">NumberOfStars=</span><span class="s">"6"</span> <span class="nt">/&gt;</span>
        <span class="nt">&lt;/StackPanel&gt;</span>
      <span class="nt">&lt;/ThemeVariantScope&gt;</span>
    <span class="nt">&lt;/StackPanel&gt;</span>
  <span class="nt">&lt;/Design.PreviewWith&gt;</span></code></pre>
</div>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_step_10_create_a_sample_to_try_out_the_custom_control">Step 10: Create a sample to try-out the custom Control</h3>
<div class="paragraph">
<p>In Avalonia an external <code>ResourceDictionary</code>-file needs to be added via <code>ResourceInclude</code> into the <code>Resources</code>-node of your choice before it gets applied. We will add it into <code>App.Resources</code> as shown below:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="xml">  <span class="nt">&lt;Application.Resources&gt;</span>
    <span class="nt">&lt;ResourceDictionary&gt;</span>
      <span class="nt">&lt;ResourceDictionary.MergedDictionaries&gt;</span>
        <span class="c">&lt;!-- Don't miss this line --&gt;</span>
        <span class="nt">&lt;ResourceInclude</span> <span class="na">Source=</span><span class="s">"/Styles/RatingStyles.axaml"</span><span class="nt">/&gt;</span>
      <span class="nt">&lt;/ResourceDictionary.MergedDictionaries&gt;</span>
    <span class="nt">&lt;/ResourceDictionary&gt;</span>
  <span class="nt">&lt;/Application.Resources&gt;</span></code></pre>
</div>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
You need to do this for every project where you want to use this control. You will not see any custom control if you forgot to add this line.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Now we can use the control in any view like shown below:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="xml"><span class="nt">&lt;Window</span> <span class="na">xmlns=</span><span class="s">"https://github.com/avaloniaui"</span>
        <span class="na">xmlns:x=</span><span class="s">"http://schemas.microsoft.com/winfx/2006/xaml"</span>
        <span class="err">[...]</span>
        <span class="na">xmlns:controls=</span><span class="s">"using:RatingControlSample.Controls"</span>
		<span class="err">[...]</span>
        <span class="na">Title=</span><span class="s">"RatingControlSample"</span><span class="nt">&gt;</span>

    <span class="nt">&lt;Design.DataContext&gt;</span>
        <span class="nt">&lt;vm:MainWindowViewModel/&gt;</span>
    <span class="nt">&lt;/Design.DataContext&gt;</span>

	<span class="nt">&lt;StackPanel</span> <span class="na">Spacing=</span><span class="s">"5"</span> <span class="na">Margin=</span><span class="s">"10"</span><span class="nt">&gt;</span>
        [...]
		<span class="nt">&lt;controls:RatingControl</span> <span class="na">NumberOfStars=</span><span class="s">"{Binding NumberOfStars}"</span>
								<span class="na">Value=</span><span class="s">"{Binding RatingValue}"</span> <span class="nt">/&gt;</span>
	<span class="nt">&lt;/StackPanel&gt;</span>
<span class="nt">&lt;/Window&gt;</span></code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
For the complete sample including the <code>ViewModel</code> please see the source code of this sample.
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_step_11_see_it_in_action">Step 11: See it in action</h3>
<div class="paragraph">
<p>We are all done. Hit [Run] or [Debug] in your IDE and you can see the control in action.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="_docs/img/result.png" alt="Rating Control in action">
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_related">Related</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This sample has shown some basics about custom controls. If you want to use this control in production you may want to improve it further, for example:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Add it into a controls library</p>
</li>
<li>
<p>Add keyboard support</p>
</li>
<li>
<p>Add unit tests</p>
</li>
<li>
<p>Add animations</p>
</li>
<li>
<p>Add an event for value changed</p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2025-11-06 19:42:36 UTC
</div>
</div>
</body>
</html>